/*
 * Copyright TLR Team, Inc. All Rights Reserved.
 */
package com.tlr.device.service;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.tfb.service.utility.AppConst;
import com.tfb.service.utility.JsonUtil;
import com.tfb.service.utility.HttpUtil;
import com.tfb.service.utility.Utils;
import com.tlr.TlrDeviceException;
import com.tlr.device.model.Eban;
import com.tlr.device.model.EbanDay;
import com.tlr.device.model.EbanTime;
import com.tlr.device.model.EbanUser;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.ws.rs.core.MediaType;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;

@SuppressWarnings("UseSpecificCatch")
public class EbanDeviceAPI implements AppConst {

    private static final Logger log = Logger.getLogger(EbanDeviceAPI.class);
    private boolean throwException;

    public EbanDeviceAPI(boolean throwException) {
        this.throwException = throwException;
    }

    public EbanDeviceAPI() {
    }

    public static SimpleDateFormat getDayFormat() {
        return new SimpleDateFormat("yyyy-MM-dd");
    }

    public static SimpleDateFormat getTimeFormat() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }

    private String getLogonUrl(String userName, String password) throws UnsupportedEncodingException {
        return String.format(EBAN_LOG_ON, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8), userName, password);
    }

    private String getOpenIDLogonUrl(String openid) throws UnsupportedEncodingException {
        return String.format(EBAN_OPENID_LOGIN, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8), openid);
    }

    private String getDelBindUrl(String token, String code) throws UnsupportedEncodingException {
        return String.format(EBAN_DEL_BIND, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8),
                             token, code);
    }

    private String getBindDeviceUrl(String token, String code, String name, String nickname) throws UnsupportedEncodingException {
        return String.format(EBAN_BIND_DEVICE, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8),
                             token, code,
                             URLEncoder.encode(name, JsonUtil.UTF8),
                             URLEncoder.encode(nickname, JsonUtil.UTF8));
    }

    private String getLogoffUrl(String token) throws UnsupportedEncodingException {
        return String.format(EBAN_LOG_OFF, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8), token);
    }

    private String getDMessageUrl(String token, String code, String date) throws UnsupportedEncodingException {
        return String.format(EBAN_DMESSAGE, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8), token, code, date);
    }

    private String getDayRestUrl(String token, String code, String date) throws UnsupportedEncodingException {
        return String.format(EBAN_DAY_REST, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8), token, code, date);
    }

    private String getDelRelationUrl(String token, String code, String id) throws UnsupportedEncodingException {
        return String.format(EBAN_DEL_RELATION, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8),
                             token, code, id);
    }

    private String getAddRelationUrl(String token, String code, String name, String mobile) throws UnsupportedEncodingException {
        return String.format(EBAN_ADD_RELATION, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8),
                             token, code, URLEncoder.encode(name, JsonUtil.UTF8), mobile);
    }

    private String getRelationUrl(String token, String code) throws UnsupportedEncodingException {
        return String.format(EBAN_RELATIONS, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8), token, code);
    }

    private String getSDayRestUrl(String token, String code, String date) throws UnsupportedEncodingException {
        return String.format(EBAN_SDAY_REST, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8), token, code, date);
    }

    private String getTrackUrl(String token, String code, String date) throws UnsupportedEncodingException {
        return String.format(EBAN_TRACK, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8), token, code, date);
    }

    private String getTrackQueryUrl(String token, String code, String date) throws UnsupportedEncodingException {
        return String.format(EBAN_QUERY, URLEncoder.encode("header[auth_ key]", JsonUtil.UTF8),
                             URLEncoder.encode("header[token]", JsonUtil.UTF8), token, code, date);
    }

    public JsonNode getUserJson(String userName, String password) {
        String logonJsonStr = "";
        try (CloseableHttpClient httpClient = HttpUtil.createHttpClient()) {
            String logonUrl = getLogonUrl(userName, password);
            HttpGet httpGet = new HttpGet(logonUrl);
            try (CloseableHttpResponse logonResp = httpClient.execute(httpGet)) {
                logonJsonStr = EntityUtils.toString(logonResp.getEntity(), JsonUtil.UTF8);
                JsonNode logonJson = JsonUtil.createJson(logonJsonStr);
                return logonJson;
            }
        } catch (Exception e) {
            log.error("the user json: " + logonJsonStr);
            log.error(Utils.getFullException(e));
            throw new RuntimeException(e);
        }
    }

    public String openidLogin(String openid) {
        String openidLoginUrl = null, logonJsonStr = null;
        try {
            openidLoginUrl = getOpenIDLogonUrl(openid);
            try (CloseableHttpClient httpClient = HttpUtil.createHttpClient()) {
                HttpGet httpGet = new HttpGet(openidLoginUrl);
                try (CloseableHttpResponse logonResp = httpClient.execute(httpGet)) {
                    logonJsonStr = EntityUtils.toString(logonResp.getEntity(), JsonUtil.UTF8);
                    JsonNode loginRes = JsonUtil.createJson(logonJsonStr);
                    if (loginRes.get("success").asInt() == 0) {
                        log.error("Login error: " + loginRes.get("message").asText());
                        log.info("OpenID login: " + logonJsonStr);
                        return null;
                    }
                    return loginRes.get("token").asText();
                }
            }
        } catch (Exception ex) {
            log.info("OpenID login: " + logonJsonStr);
            log.info(openidLoginUrl);
            log.error(Utils.getFullException(ex));
            return null;
        }
    }

    public List<EbanUser> getUserInfoList(String userName, String password, String code) {
        JsonNode userJson = getUserJson(userName, password);
        List<EbanUser> users = new ArrayList<>();
        if (!userJson.has("user_info")) {
            log.error("Can't get userinfo: " + userJson.get("message").asText());
            if (throwException) {
                throw new TlrDeviceException(userJson.get("message").asText());
            }
        }
        Iterator<JsonNode> it = userJson.get("user_info").getElements();
        while (it.hasNext()) {
            JsonNode userInfoJson = it.next();
            EbanUser user = new EbanUser();
            user.setToken(userJson.get("token").getTextValue());
            user.setReal_name(userJson.get("real_name").getTextValue());
            user.setName(userJson.get("name").getTextValue());
            user.setAvatar(userJson.get("avatar").getTextValue());
            user.setCode(userInfoJson.get("code").getTextValue());
            user.setNickname(userInfoJson.get("nickname").getTextValue());
            user.setGender(userInfoJson.get("gender").getTextValue());
            user.setBirthday(userInfoJson.get("birthday").getTextValue());
            user.setWeight(userInfoJson.get("weight").getTextValue());
            user.setHeight(userInfoJson.get("height").getTextValue());
            user.setMobile(userInfoJson.get("mobile").getTextValue());
            user.setCasehistory(userInfoJson.get("casehistory").getTextValue());
            if (code != null && code.equals(user.getCode())) {
                users.add(user);
            } else if (code == null || code.isEmpty()) {
                users.add(user);
            }
        }
        return users;
    }

    public void logoff(String token) {
        if (token == null) {
            return;
        }
        log.info("logoff from eban: " + token);
        try (CloseableHttpClient httpClient = HttpUtil.createHttpClient()) {
            String logoffUrl = getLogoffUrl(token);
            HttpGet httpGet = new HttpGet(logoffUrl);
            try (CloseableHttpResponse logoffResp = httpClient.execute(httpGet)) {
            }
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
        }
    }

    public int delRelation(String token, String code, String id) {
        try {
            Client client = Client.create();
            String url = this.getDelRelationUrl(token, code, id);
            WebResource webResource = client.resource(url);
            String content = webResource.accept(MediaType.APPLICATION_JSON).get(String.class);
            log.info(url);
            log.info(content);
            JsonNode json = JsonUtil.createJson(content);
            log.info("del relation: " + json.get("message").asText());
            return json.get("success").asInt();
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
            return 0;
        }
    }

    public String getRelations(String token, String code) {
        try {
            Client client = Client.create();
            WebResource webResource = client.resource(getRelationUrl(token, code));
            String content = webResource.accept(MediaType.APPLICATION_JSON).get(String.class);
            log.info(content);
            return content;
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
            return null;
        }
    }

    public int addRelation(String token, String code, String name, String mobile) {
        String respStr = null;
        try {
            String addRelationUrl = getAddRelationUrl(token, code, name, mobile);
            Client client = Client.create();
            WebResource webResource = client.resource(addRelationUrl);
            respStr = webResource.accept(MediaType.APPLICATION_JSON).get(String.class);
            JsonNode json = JsonUtil.createJson(respStr);
            log.info("add relation: " + json.get("message").asText());
            return json.get("success").asInt();
        } catch (Exception ex) {
            log.error(String.valueOf(respStr));
            log.error(Utils.getFullException(ex));
        }
        return 0;
    }

    public int delBind(String token, String code) {
        String respStr = null;
        try {
            String bindDeviceUrl = getDelBindUrl(token, code);
            Client client = Client.create();
            WebResource webResource = client.resource(bindDeviceUrl);
            respStr = webResource.accept(MediaType.APPLICATION_JSON).get(String.class);
            JsonNode json = JsonUtil.createJson(respStr);
            log.info("delete bind: " + json.get("message").asText());
            return json.get("success").asInt();
        } catch (Exception ex) {
            log.error(String.valueOf(respStr));
            log.error(Utils.getFullException(ex));
        }
        return 0;
    }

    public int bindDevice(String token, String code, String name, String nickname) {
        try {
            String bindDeviceUrl = getBindDeviceUrl(token, code, name, nickname);
            Client client = Client.create();
            WebResource webResource = client.resource(bindDeviceUrl);
            String respStr = webResource.accept(MediaType.APPLICATION_JSON).get(String.class);
            JsonNode json = JsonUtil.createJson(respStr);
            log.info("bind result:" + json.get("message").asText());
            return json.get("success").asInt();
        } catch (Exception ex) {
            log.error(Utils.getFullException(ex));
        }
        return 0;
    }

    public String getDataByDay(String token, String code, String date) {
        String dayRestStr = "";
        try (CloseableHttpClient httpClient = HttpUtil.createHttpClient()) {
            String dayRestUrl = getDayRestUrl(token, code, date);
            HttpGet httpGet = new HttpGet(dayRestUrl);
            try (CloseableHttpResponse dayRestResp = httpClient.execute(httpGet)) {
                dayRestStr = EntityUtils.toString(dayRestResp.getEntity(), JsonUtil.UTF8);
                // ***************************************************************
                // TODO this is workaround for the wrong response with a '`'
                // start
                if (dayRestStr.charAt(0) != '{' && dayRestStr.charAt(0) != '[') {
                    dayRestStr = dayRestStr.substring(1);
                }
                // ***************************************************************
                return dayRestStr;
            }
        } catch (Exception e) {
            log.error("the data by day json: " + dayRestStr);
            log.error(Utils.getFullException(e));
            throw new RuntimeException(e);
        }
    }

    public EbanDay getSimpleDataByDataInfo(String token, String code, String date) {
        EbanDay day = new EbanDay();
        try {
            day.setDate(date);
            SimpleDateFormat sf = EbanDeviceAPI.getDayFormat();
            SimpleDateFormat sf2 = getTimeFormat();
            Date dt = sf.parse(date);
            String dataRest = getDataByDay(token, code, date);
            JsonNode dayRestJson = JsonUtil.createJson(dataRest);
            JsonNode dayRestDetail = dayRestJson.get("detail_rest");
            JsonNode messageNode = dayRestJson.get("message");
            dataRest = dayRestDetail != null ? dayRestDetail.asText() : null;
            day.setMessage(messageNode != null ? messageNode.asText().replaceAll("【", "#【") : "无数据");
            if (dataRest != null && !dataRest.isEmpty()) {
                String[] timeSeg = dataRest.split(",");
                for (String time : timeSeg) {
                    String[] segs = time.split("-");
                    EbanTime eTime = new EbanTime();
                    long start = dt.getTime() + Long.parseLong(segs[0]) * 1000;
                    long end = dt.getTime() + Long.parseLong(segs[1]) * 1000;
                    eTime.setStart(sf2.format(new Date(start)));
                    eTime.setEnd(sf2.format(new Date(end)));
                    eTime.setContent(segs[2]);
                    eTime.setAction(segs[3]);
                    day.getTimes().add(eTime);
                }
            }
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
        }
        return day;
    }

    public EbanDay getDataByDayInfo(String token, String code, String date) throws Exception {
        try {
            SimpleDateFormat sf = EbanDeviceAPI.getDayFormat();
            SimpleDateFormat sf2 = getTimeFormat();
            Date dt = sf.parse(date);
            Calendar c = Calendar.getInstance();
            c.setTime(dt);
            c.add(Calendar.DAY_OF_MONTH, 1);
            date = sf.format(dt);
            int counter = COUNTER;
            EbanDay day = new EbanDay();
            do {
                String dataRest = getDataByDay(token, code, date);
                JsonNode dayRestJson = JsonUtil.createJson(dataRest);
                JsonNode dayRestDetail = dayRestJson.get("detail_rest");
                JsonNode messageNode = dayRestJson.get("message");
                dataRest = dayRestDetail != null ? dayRestDetail.asText() : null;
                day.setMessage(messageNode != null ? messageNode.asText().replaceAll("【", "#【") : "无数据");
                if (dataRest != null && !dataRest.isEmpty()) {
                    String[] timeSeg = dataRest.split(",");
                    for (String time : timeSeg) {
                        String[] segs = time.split("-");
                        EbanTime eTime = new EbanTime();
                        dt = sf.parse(date);
                        long start = dt.getTime() + Long.parseLong(segs[0]) * 1000;
                        long end = dt.getTime() + Long.parseLong(segs[1]) * 1000;
                        eTime.setStart(sf2.format(new Date(start)));
                        eTime.setEnd(sf2.format(new Date(end)));
                        eTime.setContent(segs[2]);
                        eTime.setAction(segs[3]);
                        day.getTimes().add(eTime);
                    }
                    day.setDate(date);
                    break;
                } else {
                    dt = sf.parse(date);
                    c = Calendar.getInstance();
                    c.setTime(dt);
                    c.add(Calendar.DAY_OF_MONTH, -1);
                    date = sf.format(c.getTime());
                }
            } while (--counter > 0);
            return day;
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
            throw e;
        }
    }

    public String getDMessage(String token, String code, String date) throws Exception {
        try (CloseableHttpClient httpClient = HttpUtil.createHttpClient()) {
            SimpleDateFormat sf = EbanDeviceAPI.getDayFormat();
            Date dt = sf.parse(date);
            // Calendar c = Calendar.getInstance();
            // c.setTime(dt);
            // c.add(Calendar.DAY_OF_MONTH, 1);
            String dayMessageUrl = getDMessageUrl(token, code, sf.format(dt));
            HttpGet httpGet = new HttpGet(dayMessageUrl);
            try (CloseableHttpResponse dayMessageResp = httpClient.execute(httpGet)) {
                String dayMessageStr = EntityUtils.toString(dayMessageResp.getEntity(), JsonUtil.UTF8);
                // ***************************************************************
                // TODO this is workaround for the wrong response with a '`'
                // start
                if (dayMessageStr.charAt(0) != '{' && dayMessageStr.charAt(0) != '[') {
                    dayMessageStr = dayMessageStr.substring(1);
                }
                // ***************************************************************
                return dayMessageStr;
            }
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
            throw e;
        }
    }

    public String getSDayRest(String token, String code, String date) throws IOException {
        try (CloseableHttpClient httpClient = HttpUtil.createHttpClient()) {
            String sDayRestUrl = getSDayRestUrl(token, code, date);
            HttpGet httpGet = new HttpGet(sDayRestUrl);
            try (CloseableHttpResponse sDayRestResp = httpClient.execute(httpGet)) {
                String sDayRestStr = EntityUtils.toString(sDayRestResp.getEntity(), JsonUtil.UTF8);
                // ***************************************************************
                // TODO this is workaround for the wrong response with a '`'
                // start
                if (sDayRestStr.charAt(0) != '{' && sDayRestStr.charAt(0) != '[') {
                    sDayRestStr = sDayRestStr.substring(1);
                }
                // ***************************************************************
                return sDayRestStr;
            }
        }
    }

    public String getSimpleLastSDay(String token, String code, String date) throws IOException, ParseException {
        SimpleDateFormat sf = EbanDeviceAPI.getDayFormat();
        Date dt = sf.parse(date);
        Calendar c = Calendar.getInstance();
        c.setTime(dt);
        date = sf.format(c.getTime());
        String sDayRest = getSDayRest(token, code, date);
        JsonNode sDayRestJson = JsonUtil.createJson(sDayRest);
        JsonNode message = sDayRestJson.get("message");
        if (JsonUtil.isNotNull(message) && message.getElements().hasNext()) {
            message = message.get(message.size() - 1);
            return message.toString();
        }
        return "\"无数据\"";
    }

    public String getLastSDay(String token, String code, String date) throws IOException, ParseException {
        SimpleDateFormat sf = EbanDeviceAPI.getDayFormat();
        Date dt = sf.parse(date);
        Calendar c = Calendar.getInstance();
        c.setTime(dt);
        c.add(Calendar.DAY_OF_MONTH, 1);
        int counter = COUNTER;
        date = sf.format(c.getTime());
        String sDayRest = null;
        JsonNode sDayRestJson = null;
        do {
            sDayRest = getSDayRest(token, code, date);
            sDayRestJson = JsonUtil.createJson(sDayRest);
            JsonNode message = sDayRestJson.get("message");
            if (JsonUtil.isNotNull(message) && message.getElements().hasNext()) {
                message = message.get(message.size() - 1);
                return message.toString();
            }
            dt = sf.parse(date);
            c = Calendar.getInstance();
            c.setTime(dt);
            c.add(Calendar.DAY_OF_MONTH, -1);
            date = sf.format(c.getTime());
        } while (--counter > 0);
        return sDayRestJson != null ? JsonUtil.writeValue(sDayRestJson) : sDayRest;
    }

    public String getQuery(String token, String code, String date) throws IOException {
        try (CloseableHttpClient httpClient = HttpUtil.createHttpClient()) {
            String trackQueryUrl = getTrackQueryUrl(token, code, date);
            HttpGet httpGet = new HttpGet(trackQueryUrl);
            try (CloseableHttpResponse trackResp = httpClient.execute(httpGet)) {
                String trackStr = EntityUtils.toString(trackResp.getEntity(), JsonUtil.UTF8);
                // ***************************************************************
                // TODO this is workaround for the wrong response with a '`'
                // start
                if (trackStr.charAt(0) != '{' && trackStr.charAt(0) != '[') {
                    trackStr = trackStr.substring(1);
                }
                // ***************************************************************
                return trackStr;
            }
        }
    }

    public String getTrack(String token, String code, String date) throws IOException {
        try (CloseableHttpClient httpClient = HttpUtil.createHttpClient()) {
            String trackUrl = getTrackUrl(token, code, date);
            HttpGet httpGet = new HttpGet(trackUrl);
            try (CloseableHttpResponse trackResp = httpClient.execute(httpGet)) {
                String trackStr = EntityUtils.toString(trackResp.getEntity(), JsonUtil.UTF8);
                // ***************************************************************
                // TODO this is workaround for the wrong response with a '`'
                // start
                if (trackStr.charAt(0) != '{' && trackStr.charAt(0) != '[') {
                    trackStr = trackStr.substring(1);
                }
                // ***************************************************************
                return trackStr;
            }
        }
    }

    public String getLastTrack(String token, String code, String date) throws IOException, ParseException {
        SimpleDateFormat sf = EbanDeviceAPI.getDayFormat();
        JsonNode trackJson = null;
        String trackJsonStr = "";
        try {
            trackJsonStr = getTrack(token, code, date);
            trackJson = JsonUtil.createJson(trackJsonStr);
            if (trackJson.get("success").getIntValue() == 1) {
                JsonNode trackNode = trackJson.get("track");
                if (JsonUtil.isNotNull(trackNode) && trackNode.getElements().hasNext()) {
                    JsonNode lastTrackNode = trackNode.get(0);
                    return lastTrackNode.toString();
                }
            }
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
            log.error("getTrack json: " + trackJsonStr);
        }
        return trackJson != null ? JsonUtil.writeValue(trackJson) : trackJsonStr;
    }

    public Eban getLastDeviceData(String token, String code, String date) {
        Eban ban = new Eban();
        String lastData = null;
        try {
            ban.setStatus_label("设备正常");
            ban.setStatus(1);
            ban.setEnabled(true);
            ban.setDevice_id(code);
            lastData = getLastTrack(token, code, date);
            log.info("Legacy API get data:" + lastData);
            JsonNode data = JsonUtil.createJson(lastData);
            if (!data.has("longitude") || !data.has("latitude")) {
                ban.setError("[Position API result: " + lastData + "]");
                log.error("No longitude and latitude information");
                ban.setStatus_label("设备不正常，无法取到位置信息");
                ban.setStatus(0);
                ban.setEnabled(false);
            } else {
                ban.setLng(data.get("longitude").asText());
                ban.setLat(data.get("latitude").asText());
            }
            if (data.has("position")) {
                ban.setPosition(data.get("position").asText());
            } else {
                log.error("No position information");
            }
            String point = getLastSDay(token, code, date);
            log.info("Legacy API get point:" + point);
            JsonNode pointNode = JsonUtil.createJson(point);
            if (pointNode.has("point"))
                ban.setPoint(pointNode.get("point").asText());
            else {
                ban.setError(ban.getError() != null ? ban.getError() + ", [Point API result: " + point + "]" : "[Point API result: " + point + "]");
                log.error("No point information");
                ban.setPoint("0");
            }
            if (pointNode.has("dateline")) {
                ban.setTime(pointNode.get("dateline").asText());
            }
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
            ban.setEnabled(false);
            ban.setError("Error data ### : ### " + lastData);
            ban.setStatus_label("设备数据获取失败");
            ban.setStatus(0);
        }
        return ban;
    }
}
